/*
 * Copyright (C) RT, 2017 by kiterunner_t
 * TO THE HAPPY FEW
 */

package com.zgys.mes.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


// 用于访问对象内的私有方法
// 1) 参数中基本类型不为包装类型，直接使用invoke
// 2) 参数中基本类型为包装类型，直接使用invokeObject
// 3) 暂时不支持传参为null的私有方法调用
//
// void methodName(int a) {}
// int param1 = 1;
// ClassUtil.invoke(obj, "methodName", param1);
//
// void methodName(Integer a) {}
// Integer param1 = 1;
// ClassUtil.invokeObject(obj, "methodName", param1);
public class ClassUtil {
    public static <T> T invoke(Object instance, String method, Object... args) {
        return invokeMethod(false, instance, method, args);
    }


    public static <T> T invokeObject(Object instance, String method, Object... args) {
        return invokeMethod(true, instance, method, args);
    }


    private static <T> T invokeMethod(boolean isObj, Object instance, String methodName, Object... args) {
        try {
            Class[] classTypes = null;
            if (args != null) {
                classTypes = new Class[args.length];
                for (int i = 0; i < args.length; i++) {
                    Class type = args[i].getClass();
                    classTypes[i] = getClassType(isObj, type);
                }
            }

            return (T) getMethod(instance, methodName, classTypes).invoke(instance, args);

        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
            throw new RuntimeException(ex);
        }
    }


    private static Class getClassType(boolean isObj, Class type) {
        if (isObj) {
            return type;
        }

        Class retType = type;
        if (type == Integer.class) {
            retType = Integer.TYPE;

        } else if (type == Long.class) {
            retType = Long.TYPE;

        } else if (type == Double.class) {
            retType = Double.TYPE;

        } else if (type == Float.class) {
            retType = Float.TYPE;

        } else if (type == Boolean.class) {
            retType = Boolean.TYPE;

        } else if (type == Character.class) {
            retType = Character.TYPE;

        } else if (type == Void.class) {
            retType = Void.TYPE;

        } else if (type == Short.class) {
            retType = Short.TYPE;
        }

        return retType;
    }


    private static Method getMethod(Object instance, String methodName, Class[] classTypes)
            throws NoSuchMethodException {

        Method accessMethod = getMethod(instance.getClass(), methodName, classTypes);
        accessMethod.setAccessible(true);
        return accessMethod;
    }


    private static Method getMethod(Class thisClass, String methodName, Class[] classTypes)
            throws NoSuchMethodException {

        try {
            return thisClass.getDeclaredMethod(methodName, classTypes);
        } catch (NoSuchMethodException e) {
            return getMethod(thisClass.getSuperclass(), methodName, classTypes);
        }
    }
}

